import java.awt.Image;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.Stack;

import javax.swing.JOptionPane;

import javax.swing.Icon;
import javax.swing.ImageIcon;

public class Uno {
	private static final long serialVersionUID = 6825507269065686842L;

	protected final static int INITIAL_HAND_SIZE = 6; // hand size - 1

	protected static ArrayList<Player> playerList;
	protected static Player UnoCaller;
	protected static int playerCount = 2;
	protected static Deck deck;
	private static Stack<Card> discardPile;
	private static Card wildColorCard;// this holds the color card that the
										// player chooses
	private static boolean wildPlayed = false;
	private static boolean calledUno = false;

	/*
	 * direction: true = clockwise (cw) false = counterclockwise (ccw)
	 */
	protected static boolean direction = true;
	private static boolean isPlayerSkipped = false;
	private static int playerIndex = 0;

	private static boolean powercard = false;

	private static int winnersPoints = 0;

	private static String unoWinner;

	/**
	 * default constructor
	 */
	public Uno() {
		super();
		playerList = new ArrayList<Player>();
		deck = new Deck();
		direction = true;
		discardPile = new Stack<Card>();
		direction = true;
		isPlayerSkipped = false;
		playerIndex = 0;
	}

	public static void main(String[] args) {
		Uno uno = new Uno();
		dummyMethodToSuppressWarning(uno);
		unoNew();
		discardPile.add(deck.remove());
	}

	private static void dummyMethodToSuppressWarning(Uno uno) {
		// this is a declaration suppression method
		// of the highest importance for OCD

	}

	static void checkStateRoundOrLaunch() {

		if (roundState()) {
			calculatePoints();
			addPointsToWinner();
			gameState();
		}
		/**
		 * send first layer to UnoLogin_UI display information
		 */
		if (roundState() == true) {
			displayWinner("won that round!\nGet ready for the next round");
			resetPlayerIndex();
			setUpNewRound();
			discardPile.add(deck.remove());
			dealPlayerHand(playerList);
			Uno_UI.launch(playerList.get(playerIndex));
		} else {
			Uno_UI.launch(playerList.get(playerIndex));
			System.out.println("checkStateRoundOrLaunch() continuing");
		}
	}

	private static void resetPlayerIndex() {
		System.out.println("resetPlayerIndex()");
		isPlayerSkipped = false;
		playerIndex = 0;
	}

	/**
	 * should make a better UI
	 */
	private static void displayWinner(String msg) {
		System.out.println("displayWinner()");
		String message = String.format("%s " + msg, getUnoWinner());
		JOptionPane.showMessageDialog(null, message);
	}

	/**
	 * @param uno
	 *            - merely added so won't throw a fit for unused declaration
	 */
	protected static void unoNew() {
		UnoNew_UI.launch();
	}

	protected static void startGame() {
		System.out.println("startGame()");
		dealPlayerHand(playerList);
		checkStateRoundOrLaunch();
	}

	/**
	 * 
	 * @return
	 */
	public static void getNextPlayer() {
		if (direction) {
			++playerIndex;
			if (playerIndex == playerList.size())
				playerIndex = 0;
		} else {
			--playerIndex;
			if (playerIndex < 0)
				playerIndex = playerList.size() - 1;
		}
	}

	public static String getUnoWinner() {
		return unoWinner;
	}

	public static void setUnoWinner(String unoWinner) {
		Uno.unoWinner = unoWinner;
	}

	/**
	 * deal each player 7 cards
	 * 
	 * @param playerList2
	 */
	private static void dealPlayerHand(ArrayList<Player> playerList2) {
		for (Player player : playerList2) {
			for (int i = 0; i <= INITIAL_HAND_SIZE; i++) {
				player.setPlayerHand(deck.remove());
			}
		}
	}

	/**
	 * this method gets the players from the UnoPlayer_UI class
	 * 
	 * @param player
	 */
	protected static void addPlayerToGame(ArrayList<Player> player) {
		playerList.addAll(player);
	}

	private static void gameState() {
		// step through playerList in a loop
		// check player points = 500, game over if player points 500

		for (Player player : playerList) {
			if (player.getPoints() > 500) {
				displayWinner("won Uno!");
				System.exit(0);
			}
		}
	}

	private static boolean roundState() {
		// step through playerList in a loop
		// check player hand = 0, round over if player card count = 0
		// reset round if true - instantiate new deck etc, etc
		for (Player player : playerList) {
			if (player.getPlayerHandSize() == 0) {
				return true;
			}
		}
		return false;
	}

	private static void setUpNewRound() {
		// remove the players cards and shuffles discardpile
		System.out.println("setUpNewRound()");
		resetPoints();
		deck = new Deck();
		for (Player player : playerList) {
			player.removeCards();
		}
	}

	private static void calculatePoints() {
		System.out.println("calculatePoints()");
		for (Player player : playerList) {
			for (int i = 0; i < player.getPlayerHandSize(); i++) {
				winnersPoints += player.getPlayerHand().get(i).getValue();
			}
		}
	}

	private static void addPointsToWinner() {
		System.out.println("addPointsToWinner()");

		for (Player player : playerList) {
			if (player.getName().equals(unoWinner)) {
				player.setPoints(winnersPoints);
			}
		}

	}

	private static void resetPoints() {
		winnersPoints = 0;
	}

	/**
	 * 
	 * @param card
	 */
	protected static void checkPowerCard(Card card) {
		/**
		 * Check size of stack set the size if the size has not changed ignore
		 * 
		 * if power card == true check card set powercard to false. power card
		 * only get back to true if a card is played.
		 */

		if (powercard = true) {
			/**
			 * if the card is a reverse
			 */
			if (card.getFace() == 10) {
				reverse();
				powercard = false;
			}

			/**
			 * if the card is a skip
			 */
			if (card.getFace() == 11) {
				skip();
				powercard = false;
			}

			/**
			 * if the card is a draw 2 card
			 */
			if (card.getFace() == 12) {
				draw2();
				powercard = false;
			}

			/**
			 * if the card is a wild
			 */
			if (card.getFace() == 13) {
				powercard = false;
			}
			/**
			 * if the card is a wild4
			 */
			if (card.getFace() == 14) {
				wild4();
				powercard = false;
			}
		}
	}

	public static boolean isPowercard() {
		return powercard;
	}

	public static void setPowercard(boolean powercard) {
		Uno.powercard = powercard;
	}

	protected static void reverse() {
		// affect next player
		if (direction) {
			direction = false;
		} else {
			direction = true;
		}

		/**
		 * handles reverse for two players
		 */
		if (playerList.size() == 2) {
			getNextPlayer();
		}
	}

	private static void draw2() {
		// affect next player
		// isPlayerSkipped = true;
		skip();
		draw(2);
	}

	private static void skip() {
		getNextPlayer();
	}

	private static void wild4() {
		skip();
		draw(4);
	}

	public static void draw(int numberOfCards) {
		for (int i = 0; i < numberOfCards; i++) {
			playerList.get(playerIndex).setPlayerHand(getCardFromDeck());
		}
	}

	/**
	 * @return the serialversionuid
	 */
	public static long getSerialversionuid() {
		return serialVersionUID;
	}

	/**
	 * image toolkit resource helper
	 * 
	 * @param name
	 *            - image name w/o extension
	 * @return toolkit resource image
	 */
	public static Image getImage(String name) {
		return Toolkit.getDefaultToolkit().getImage(
				Uno_UI.class.getResource("/images/" + name + ".png"));
	}

	/**
	 * image toolkit resource helper
	 * 
	 * @param name
	 *            - image name w/o extension
	 * @return toolkit resource icon
	 */
	public static Icon getIcon(String name) {
		return new ImageIcon(Toolkit.getDefaultToolkit().getImage(
				Uno_UI.class.getResource("/images/" + name + ".png")));
	}

	public static Card getCardFromDeck() {
		Card card = deck.remove();
		return card;
	}

	public static void addCardToPile(Card card) {
		discardPile.add(card);
	}

	public static Card getDiscard() {
		Card discard = discardPile.peek();
		return discard;
	}

	public static boolean isPlayerSkipped() {
		return isPlayerSkipped;
	}

	public static void setPlayerSkipped(boolean isPlayerSkipped) {
		Uno.isPlayerSkipped = isPlayerSkipped;
	}

	/**
	 * @return the playerList
	 */
	public static ArrayList<Player> getPlayerList() {
		return playerList;
	}

	/**
	 * @return the playerIndex
	 */
	public static int getPlayerIndex() {
		return playerIndex;
	}

	public static Player getCurrentPlayer() {
		return playerList.get(playerIndex);
	}

	/**
	 * this returns hold the chosen color card
	 * 
	 * @return
	 */
	public static Card getWildCard() {
		return wildColorCard;
	}

	public static void setWildCard(Card card) {
		wildColorCard = card;
	}

	public static boolean isWildPlayed() {
		return wildPlayed;
	}

	public static void setWildPlayed(boolean wildPlayed) {
		Uno.wildPlayed = wildPlayed;
	}

	public static void setUnoCaller(Player unoCallers) {
		Uno.UnoCaller = unoCallers;
	}

	public static Player getUnoCaller() {
		return UnoCaller;
	}

	public static boolean hasCalledUno() {
		return calledUno;
	}

	public static void setCalledUno(boolean calledUno) {
		Uno.calledUno = calledUno;
	}

	/**
	 * Draws to cards to the Uno caller
	 */
	public static void penalizeUnoCaller() {
		for (Player player : playerList) {
			if (player.equals(UnoCaller)) {
				for (int i = 0; i <= 1; i++) {
					player.setPlayerHand(getCardFromDeck());
				}
			}
		}
	}

	/**
	 * @return the direction
	 */
	public static boolean isDirection() {
		return direction;
	}

	/**
	 * @param direction
	 *            the direction to set
	 */
	public static void setDirection(boolean direction) {
		Uno.direction = direction;
	}
}
